package base_app

import (
	baseLog "gitee.com/zaiqiang231/go-base-app/base_app/log"
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cobra"
	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/viper"
	"go.uber.org/zap/zapcore"
	"os"
	"path/filepath"
	"strings"
)

type App struct {
	basename       string
	cmd            *cobra.Command
	cmdDescription string
	cmdArgs        cobra.PositionalArgs
	runFunc        RunFunc
}

type AppOption func(*App)

func WithCmdDescription(desc string) AppOption {
	return func(a *App) {
		a.cmdDescription = desc
	}
}

func WithCmdValidArgs(args cobra.PositionalArgs) AppOption {
	return func(a *App) {
		a.cmdArgs = args
	}
}

type RunFunc func(basename string) error
type InitConfigFunc func(a *App)

func WithRunFunc(run RunFunc) AppOption {
	return func(app *App) {
		app.runFunc = run
	}
}

func NewApp(basename string, initConfig InitConfigFunc, opts ...AppOption) *App {
	a := &App{
		basename: basename,
	}

	for _, opt := range opts {
		opt(a)
	}

	a.buildCommand(initConfig)

	return a
}

var isDebug bool
var config string
var showThirdPartyLog bool
var testHealth bool

// 初始化命令
func (a *App) buildCommand(initConfig InitConfigFunc) {
	cmd := cobra.Command{
		Use:           a.basename,
		Short:         a.basename,
		Long:          a.cmdDescription,
		Args:          a.cmdArgs,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	if a.runFunc != nil {
		cmd.RunE = a.runCommand
	}

	cmd.SetOut(os.Stdout)
	cmd.SetErr(os.Stderr)
	cmd.Flags().SortFlags = true
	cmd.PersistentFlags().BoolVarP(&isDebug, "debug", "d", false, "设置模式")
	cmd.PersistentFlags().StringVarP(&config, "config", "c", "", "配置文件路径")
	cmd.PersistentFlags().BoolVarP(&showThirdPartyLog, "show-third-party-log", "", false, "配置第三方打印日志")
	cmd.PersistentFlags().BoolVarP(&testHealth, "test-health", "", false, "启动时检测健康")

	cobra.OnInitialize(func() {
		if showThirdPartyLog {
			jww.SetLogThreshold(jww.LevelDebug)
			jww.SetStdoutThreshold(jww.LevelDebug) //设置jww 的日志输出等级
		}

		if config != "" {
			viper.SetConfigFile(config)
		} else {
			viper.AddConfigPath(".") // optionally look for config in the working directory
			if names := strings.Split(a.basename, "-"); len(names) > 1 {
				viper.AddConfigPath(filepath.Join("/etc", names[0]))
				viper.AddConfigPath("$HOME/." + names[0])
			}
			viper.SetConfigName(a.basename) // name of config file (without extension)
		}

		if err := viper.ReadInConfig(); err != nil {
			baseLog.Fatalf("Error: viper failed to read configuration file: %v", err)
		}

		initConfig(a)

		if isDebug {
			baseLog.DefaultLogger.Loglevel.SetLevel(zapcore.DebugLevel)
			gin.SetMode(gin.DebugMode)
		} else {
			baseLog.DefaultLogger.Loglevel.SetLevel(zapcore.InfoLevel)
			gin.SetMode(gin.ReleaseMode)
		}

		if showThirdPartyLog {
			viper.Debug()
		}
	})

	a.cmd = &cmd
}

func (a *App) runCommand(cmd *cobra.Command, args []string) error {

	// run application
	if a.runFunc != nil {
		return a.runFunc(a.basename)
	}
	return nil
}

// Run 运行命令
func (a *App) Run() {
	if err := a.cmd.Execute(); err != nil {
		baseLog.Fatalf("%v %v", color.RedString("Error:"), err)
	}
}
